wildpocketsfandomcom-20200213-history
Programming Manual Chap. 08
Physics Basics The physics system is a rigid-body simulator supporting joints and collision detection. A "physics step" does several things: it calculates forces on objects, it applies forces to the objects updating their velocities, and it detects whether or not objects have interpenetrated. When the lua script and the physics system are both manipulating an object, they take turns. The following chapters describe the physics system in more detail. Physics Forces Physics Basics By default, when you put a scene object into the world, the physics system will immediately begin operating on it. The object will fall to the ground, roll around and eventually come to a stop. If it bumps into something along the way, it will ricochet and impart inertia to the object it collided with. The Interaction of Physics and Script The Lua script and the physics subsystem are both capable of moving objects around. In effect, they take turns doing this: the physics system gets a chance to move the objects a tiny amount, then the script can examine the objects and move them a little. This coeexistence is governed by the game's timer queue. As mentioned in a previous chapter, the Lua script can schedule tasks to be performed at specific times. The physics system uses this same mechanism to request physics updates at a rate of exactly 240 updates per second. It is in-between these steps that the Lua script can examine the objects and alter them, if desired. Collision Volumes It would theoretically be possible for the physics system to check whether or not two objects have collided by comparing their polygons. However, polygon comparisons are very expensive and slow. Instead, Wild Pockets uses "collision spheres" and "collision boxes." When the 3D modeler creates the model, he adds a set of spheres and boxes to the model that, when taken together, roughly approximate its shape. The collision volumes are considered a part of the 3D model. The physics system treats the object as if it were this set of boxes and spheres. It entirely ignores the polygons. One of the side effects of using collision volumes is that an object can be modeled with collision volumes that don’t match its shape very well. In this case, you might get some odd movement out of the object. To see the collision volumes for objects in the scene, you can use the following function: SceneManager.showCollisions(true) It is also possible to model an object that has no collision volumes at all. In that case, the object is effectively insubstantial. Such objects will be created with physics disabled by default. If you reenable physics, they will fall right through the floor. Disabling Physics Sometimes, you write a game that really has nothing whatsoever to do with physics forces. For example, if you were to write a game of space invaders, physics forces wouldn't be very helpful - in fact, it would be a real nuisance to keep the space invaders from falling under the force of gravity. In these situations, it may be best to simply turn off different parts of the physics system. There are two parts of the physics system that can be turned off: physics force calculation, and collision detection. Physics force calculation allows an object to have accumulated forces acting on it every frame, these can come from applyForce calls from the scripting layer, collisions, and gravity. Also simply updating an objects position because of an initial velocity is done via the physics engine. If the Path system, or a seperate method of object interaction using setPositions is being used, the physics engine force calculation is not needed. To shut it off use: SceneObject:setAnchored(true) Objects that are anchored still execute collision detection and can still affect the physics of other objects, but are not themselves affected by physics. Anchoring is also useful for scenery and for heavy, solid objects that shouldn't be pushed by other objects, such as a stone wall with a tennis ball bouncing off of it. When an object is anchored, the following functions will have no effect on it: applyForce, applyImpulse, applyTorque, setVelocity, and setAngularVelocity. Collision detection is simply the detection of weather a collision is happening. Disabling collision can be very useful in games where objects aren't directly interacting with each other. When writing a game like majhong where multiple tiles may be constantly touching each other, but with no need to affect each other, the collision detection of the physics engine can drag down the speed of the game. Disabling collision detection will allow the objects to move around freely but not move other tiles. To disable collision detection use: SceneObject:setCollidable(false) With collision detection disabled on an object, it will not be able to collide with any other object in the scene and will simply pass through the other object without firing a collision handler or applying any force to either object. However, non-collidable objects are still governed by forces such as gravity, or calls from the scripting layer to apply different forces. To completely remove an object from the physics system, use both SceneObject:setAnchored(true) and SceneObject:setCollidable(false). This will result in stationary objects that all other objects pass through. Turning off physics doesn't stop you from moving an object procedurally: setPosition and other functions that directly affect an objects transform will still work. Collision Handlers Collision Handling Basics When two objects collide, it may be desirable to run some Lua code. For example, when a ball hits a wall, you might want to play a "boing" sound. To make things happen when objects collide, you will need a collision handler. Note: if SceneObject:setCollidable(false) is set on either object, neither the collision handler on the object with setCollidable(false) or the object it's hitting will call their collision handler. setCollidable(false) effectively drops out all collision checking from that object, so it is as if the object doesn't exist in the collision system at all. If you would like objects to pass through each other, but still call collision handlers when they do, see Pass Through below. Collision is NOT Instantaneous Before we tell you how collision handlers work, we need to explain what collisions actually are: two objects that have very slightly penetrated each other. As soon as the two objects interpenetrate, the physics system starts applying forces to shove the two objects apart. But it may take a little time for the forces to cause the two objects to separate. Because of this, collisions actually have three stages: *'start': the objects have just interpenetrated each other. *'continue': the objects have been interpenetrating for a while, and the physics system is still trying to shove them apart. *'end': the physics system has finally succeeded in pushing the objects apart. They are no longer interpenetrating. This is important: you need to remember that collision is not an instantaneous process - it can take quite a bit of time, in computer terms. Collision Handlers A collision handler is a Lua function that gets called when two objects collide. It takes three arguments: the first object (the object which has the collision handler set), the second object (the object against which it is colliding), and the stage, as described above. The stage is a string which can be "start", "continue", or "end". The collision handler must be attached to a particular object using setCollisionHandler. Here is a simple example, which causes a ball to play a sound when it collides with something: function boingCollisionFunction(obj1, obj2, stage) if (stage "start") then Sound.new("alice/boing.mp3"):play() end end ball:setCollisionHandler(boingCollisionFunction) In a typical collision, the collision handling function will get called many times: once at the start of the collision, several times during the continuation of the collision, and once at the end of the collision. But we only want the 'boing' sound to get played once, so we check the 'stage' parameter. We only play the sound at the start of the collision. One Collision, Two Handlers When two objects collide, if both objects have collision handlers, then both collision handlers will get called. It is not predictable what order the handlers will get called in. Sometimes, the two collision handlers may produce twice the effect you desire. For example, consider a billiards table where each ball has a collision handler that makes a "click" sound. When two balls collide, the two handlers will get called, and you'll get two clicks --- not what you want. In this case, the solution is to use a rule to disable one of the two clicks. A good rule in this case would be for the collision handler of the higher numbered ball to make the click-sound, and the collision handler for the lower-numbered ball to stay silent: function billiardBallCollisionHandler(obj1, obj2, stage) if (stage "start") then if (obj1.ballNumber > obj2.ballNumber) then Sound.new("alice/click.mp3"):play() end end end In fact, as a convenience feature, all SceneObjects can be compared against each other with <, <=, >, >=, and operators and will return mathematically consistent answers (i.e. if objectA > objectB is true, objectB < objectA is also true). This feature can be useful in writing collision handlers similar to the one above. Pass-Through When two objects interpenetrate, the physics engine immediately applies forces to push them apart. If a collision handler calls the method SceneObject:passThrough, this will suppress these separation forces. Effectively, this will cause the two objects to pass through each other like ghosts. Joints Using Joints Caution: Joints are a relatively new feature of Wild Pockets, and they still need some testing/debugging. Joints may not be reliable at this stage. It is usually wise to do without, for now. Joints are connectors between two scene objects that can keep the objects attached to each other. You can use joints to bind two or more objects together so that when one is moved, the other moves with it. To create a joint using the Joint tool in the builder, click on the first object and drag to the second object, releasing the mouse button with the mouse hovering over the desired second object. This creates a joint between the two objects. you can also create a joint in script using Joint.new and then attach it between two objects using: myJoint:setObject1(myFirstObject) myJoint:setObject2(mySecondObject) Note that it is possible to attach a joint to only one object. If this is done, the object becomes anchored to the scene itself at the joint's position. This is useful for doing things like anchoring an object to the ground or to a point in empty space. Joints also have a position and rotation for their pivots, which is specified in world coordinate space. myJoint:setPosition(desiredPosition) myJoint:setRotation(axisAlignment) While all joints have a position, only certain joints (such as hinge and universal) respect rotation. The rotation of the joint determines where the joint's free axes are aligned; hinge joints have one free axis, and the rotation goes from the global positive-y axis to the orientation of the joint's free axis. For universal joints, the rotation goes from the positive-y and positive-z global axes to the two free axes of the universal joint. Types of Joints Wild Pockets supports the following types of joints (support for more types is coming soon): You can select the type of joint in the builder, or by using the Joint:setType command. Note that changing from one joint to another can lose some information if you try to change back; for example, ball joints do not constrain on any axis and therefore have no concept of "joint rotation," so changing from a hinge to a ball and back to a hinge is likely to lose the axis of rotation on the hinge. Enabling a joint Joints must be enabled to actually effect either object they are attached to, otherwise they will just stay at their current position in world space and effectively do nothing. After attaching a joint to two objects and setting its type, you must call: Joint:setEnabled(true) This will start the joint acting on its attached objects. If you would like a joint to stop acting on its attached objects, simply call the same function, but set enabled to FALSE. This will again, leave the joint in its current position until you setEnabled(true) again. If you are done using a joint, simply call: delete(joint) '' Modifying joints: break force and angle constraints You can create joints that break when more than a certain amount of force is applied to the joint. To make a joint breakable, use the following command: ''myJoint:setBreakForce(desiredBreakForce) The break force is specified in newtons; specifying 0 denotes the joint should be unbreakable. A reasonable break force to test setBreakForce is around 4000 newtons. You can also specify a minimum and maximum deflection angle for a joint; the joint will be restricted from travelling outside the angle ranges. myJoint:setRange(min,max) The "min" and "max" deflections are relative to the joint's initial position. Note that disabling and re-enabling a joint resets its initial position when the joint is enabled.